use cookie::{time::Duration, Cookie};
use ntex::{
    http::Response,
    web::{
        types::{Json, State},
        Responder,
    },
};
use reqwest::Client;
use std::sync::Arc;

use crate::{errors::CustomError, models::user::Login, 
    models::user::AccessToken, models::user::GetHubUserInfo, AppState};

const CLIENT_ID: &str = "Ov23litkiuorIGDLbn8F";
const CLIENT_SECRET: &str = "74b9998fbdf5d058b9ac162430fff6d34ec564b3";


// /user/login 接收前端传来的 code， 获取 access_token， 获取用户信息并写入数据库
pub async fn github_login(
    code: Json<Login>,
    state: State<Arc<AppState>>,
) -> Result<impl Responder, CustomError> {
    let code = &code.code;

    println!("---------------- code: {}", code);

    //获取 access_token
    let client = Client::new();
    let access_token = client
        .post(format!(
            "https://github.com/login/oauth/access_token?client_id={CLIENT_ID}&
            client_secret={CLIENT_SECRET}&code={code}"
        ))
        .header("Accept", "application/json") // 设置Github api 返回数据类型为json
        .send()
        .await;

    let token = match access_token {
        Ok(r) => match r.json::<AccessToken>().await {
            Ok(t) => t.access_token,
            Err(_) => return Err(
                CustomError::AuthFailed("code 是无效的（可能已经过期），请从新使用 GitHub 登录".into())
            ),
        },
        Err(_) => {
            return Err(CustomError::AuthFailed("无法获取 access_token, 请重试".into()))
        }
    };

    println!("---------------- access_token: {}", token);

    //获取用户信息
    let user_info = client
        .get("https://api.github.com/user")
        .bearer_auth(token.clone())
        .header("User-Agent", "lightning")
        .send()
        .await;

    let info = match user_info {
        Ok(r) => r.json::<GetHubUserInfo>().await.unwrap(),
        Err(_) => return Err(CustomError::InternalServerError("无法获取用户信息，请重试".into())),
    };


    //设置cookie，用于后续请求不需要每次都登录
    let mut cookie = Cookie::new("ACCESS_TOKEN", token);
    cookie.set_path("/");                   // 设置cookie的路径为根目录
    cookie.set_max_age(Duration::days(7));  // 设置cookie的过期时间为7天
    cookie.set_http_only(true);             // 设置cookie为http only，防止js获取


    //写入数据库
    let db_pool = &state.db_pool;
    sqlx::query(
        "INSERT INTO users (id, name, avatar_url) VALUES (?, ?, ?)
        ON CONFLICT(id) DO UPDATE SET name = ?, avatar_url = ?;",
    )
    .bind(info.id)
    .bind(info.name.clone())
    .bind(info.avatar_url)
    .execute(db_pool)
    .await?;

    let mut response = Response::Ok().body(format!("登录成功，欢迎回来 {}!", info.name));

    let _ = response.add_cookie(cookie); // 将cookie添加到响应中, 以便前端可以保存  -  此处忽略错误

    Ok(response)
}